#ifndef __LINE_HPP
#define __LINE_HPP

#include "base/Graphic.hpp"
#include "../utils/Common.hpp"

using namespace std;

namespace model {


template<class PixelType> class Line: public base::Graphic<PixelType>, public model::base::graphic::_3D {
	
	protected:base::graphic::Point<Integer> x1y1;
	protected:base::graphic::Point<Integer> x2y2;
	
	public:Line(int x1, int y1, int x2, int y2): x1y1{x1, y1}, x2y2{x2, y2} {
	}

	public:Line(int x1, int y1, int z1, int x2, int y2, int z2): x1y1{x1, y1, z1}, x2y2{x2, y2, z2} {
	}

	private:Line(const Line& another) = delete;
	
	public:virtual void exposure(const utils::EasyMatrix<Float>& camera) = 0;
};

class CharLine: public Line<char> {
	
	private:char pixelChar;
	
	
	public:CharLine(int x1, int y1, int x2, int y2, char pixel = '+'): Line(x1, y1, x2, y2) {
		pixelChar = pixel;
		dda();
	}
	public:CharLine(int x1, int y1, int z1, int x2, int y2, int z2, char pixel = '+'): Line(x1, y1, z1, x2, y2, z2) {
		pixelChar = pixel;
		//dda();
	}

	private:CharLine(const CharLine& another) = delete;

	//数值微分法
	public:void dda(char p) {
		dda(*this, x1y1.x, x1y1.y, x2y2.x, x2y2.y, p);
	}

	public:void dda() {
		dda(pixelChar);
	}
	
	public:static void dda(base::Graphic<char>& graphic, const CharLine& line, char pixel) {
		dda(graphic, line.x1y1.x, line.x1y1.y, line.x2y2.x, line.x2y2.y, pixel);
	}
	
	public:static void dda(base::Graphic<char>& graphic, Integer x1, Integer y1, Integer x2, Integer y2, char pixel) {
		Pixel p;
		p.setPixel(pixel);
		
		Integer deltaX = getDelta(x2, x1);
		Integer deltaY = getDelta(y2, y1);
		
		if(utils::abs(deltaX) > utils::abs(deltaY)) {
			
			Float k = (Float)deltaY / (Float)deltaX;
			Float y = y1;
			Integer x  = x1;
			
			if(x < x2) for(; x <= x2; y+=k) {
				p.setX(x);
				p.setY(y + 0.5);
				graphic.putPixel(p);
				++x;
			} else for(; x >= x2; y-=k) {
				p.setX(x);
				p.setY(y + 0.5);
				graphic.putPixel(p);
				--x;
			}
		} else {
			
			Float k = (Float)deltaX / (Float)deltaY;
			Float x = x1;
			Integer y  = y1;
			
			if(y < y2) for(; y <= y2; x+=k) {
				p.setY(y);
				p.setX(x + 0.5);
				graphic.putPixel(p);
				++y;
			} else for(; y >= y2; x-=k) {
				p.setY(y);
				p.setX(x + 0.5);
				graphic.putPixel(p);
				--y;
			}
		}
	}
	
	public:Integer getDeltaX(void) {return getDelta(x2y2.x, x1y1.x);}
	public:Integer getDeltaY(void) {return getDelta(x2y2.y, x1y1.y);}
	public:Float   getK(void) {return (Float)getDeltaY() / (Float)getDeltaX();}
	
	public:Graphic& translate(const utils::EasyMatrix<Float>& matrix) {
		base::graphic::Point<Integer>::translate(x1y1, matrix);
		base::graphic::Point<Integer>::translate(x2y2, matrix);
		clear();
		dda();
		rebuff();
		return *this;
	}

	public:void exposure(const utils::EasyMatrix<Float>& camera) {
		translate(camera);
	}
};

};

#endif
